﻿using System;
using Csla;
using System.ComponentModel.DataAnnotations;
using BizControl.Resources;
using BizControl.Library.BusinessObjects.People;
using BizControl.DalLinq;
using BizControl.Library.BusinessObjects.Activities;
using Csla.Data;
using System.Data.Linq;
using Csla.Rules;
using BizControl.Library.Security;
using System.Collections;

namespace BizControl.Library.BusinessObjects.Marks
{
  [Serializable()]
  public enum ConditionType : int
  {
    Unknown = 0,
    MarkRequired = 1,
    ActivityRequired = 2,
    SkillRequired = 3
  }

  [Serializable()]
  public class MarkConditionBO : BusinessBase<MarkConditionBO>
  {
    #region Business Methods

    private static readonly PropertyInfo<Guid> MarkConditionIdProperty = RegisterProperty<Guid>(c => c.MarkConditionId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"MarkConditionIdRequiredPropertyError")]
    public Guid MarkConditionId
    {
      get { return GetProperty(MarkConditionIdProperty); }
      private set { SetProperty(MarkConditionIdProperty, value); }
    }

    private static readonly PropertyInfo<MarkInfo> MarkProperty = RegisterProperty<MarkInfo>(c => c.Mark);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"MarkConditionMarkRequiredPropertyError")]
    public MarkInfo Mark
    {
      get { return GetProperty(MarkProperty); }
      private set { SetProperty(MarkProperty, value); }
    }

    private static readonly PropertyInfo<MarkInfo> RequiredMarkProperty = RegisterProperty<MarkInfo>(c => c.RequiredMark);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkConditionRequiredMarkProperty")]
    public MarkInfo RequiredMark
    {
      get { return GetProperty(RequiredMarkProperty); }
      set { SetProperty(RequiredMarkProperty, value); }
    }

    private static readonly PropertyInfo<ActivityInfo> RequiredActivityProperty = RegisterProperty<ActivityInfo>(c => c.RequiredActivity);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkConditionRequiredActivityProperty")]
    public ActivityInfo RequiredActivity
    {
      get { return GetProperty(RequiredActivityProperty); }
      set { SetProperty(RequiredActivityProperty, value); }
    }

    private static readonly PropertyInfo<ConditionType> TypeProperty = RegisterProperty<ConditionType>(c => c.Type);
    public ConditionType Type
    {
      get { return GetProperty(TypeProperty); }
      set { SetProperty(TypeProperty, value); }
    }

    #region Common properties

    private static readonly PropertyInfo<bool> IsDeletedFieldProperty = RegisterProperty<bool>(c => c.IsDeletedField, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"IsDeletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"IsDeletedFieldProperty")]
    public bool IsDeletedField
    {
      get { return GetProperty(IsDeletedFieldProperty); }
      private set { SetProperty(IsDeletedFieldProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> LastChangedProperty = RegisterProperty<DateTime>(c => c.LastChanged);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedProperty")]
    public DateTime LastChanged
    {
      get { return GetProperty(LastChangedProperty); }
      private set { SetProperty(LastChangedProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> LastChangedByProperty = RegisterProperty<PersonInfo>(c => c.LastChangedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedByProperty")]
    public PersonInfo LastChangedBy
    {
      get { return GetProperty(LastChangedByProperty); }
      private set { SetProperty(LastChangedByProperty, value); }
    }

    private static readonly PropertyInfo<byte[]> TimeStampProperty = RegisterProperty<byte[]>(c => c.TimeStamp);
    private byte[] TimeStamp
    {
      get { return GetProperty(TimeStampProperty); }
      set { SetProperty(TimeStampProperty, value); }
    }

    #endregion

    #region methods

    private static readonly MethodInfo GetSourceElementsMethod = RegisterMethod(typeof(MarkConditionBO), "GetSourceElements");
    public IEnumerable GetSourceElements()
    {
      //CanExecuteMethod(CheckDueDateMethod);
      switch (Type)
      {
        case ConditionType.MarkRequired:
          return ((Parent as MarkConditions).Parent as ActivityMarkBO).AvailableAsConditionActivityMarks;
        case ConditionType.ActivityRequired:
          return ((Parent as MarkConditions).Parent as ActivityMarkBO).AvailableAsConditionActivities;
        case ConditionType.SkillRequired:
          return ((Parent as MarkConditions).Parent as ActivityMarkBO).AvailableAsConditionPersonMarks;
        case ConditionType.Unknown:
        default:
          break;
      }
      return null;
    }

    private ConditionType DetermineType()
    {
      if (RequiredMark != null && !RequiredMark.IsEmpty() && RequiredMark.Type == MarkType.ActivityMark) return ConditionType.MarkRequired;
      if (RequiredMark != null && !RequiredMark.IsEmpty() && RequiredMark.Type == MarkType.PersonMark) return ConditionType.SkillRequired;
      if (RequiredActivity != null && !RequiredActivity.IsEmpty()) return ConditionType.ActivityRequired;
      return ConditionType.Unknown;
    }

    #endregion

    internal void LoadData(MarkCondition markConditionDB)
    {
      if (markConditionDB == null) return;
      MarkConditionId = markConditionDB.MarkConditionId;
      this.Mark = new MarkInfo(markConditionDB.MarkRef, MarkType.ActivityMark);
      RequiredActivity = markConditionDB.RequiredActivityRef == null ? null : new ActivityInfo(markConditionDB.RequiredActivityRef);
      RequiredMark = markConditionDB.RequiredMarkRef == null ? null : new MarkInfo(markConditionDB.RequiredMarkRef, MarkInfo.GetMarkType(markConditionDB.RequiredMarkRef));
      Type = DetermineType();

      // Common properties
      CreatedBy = new PersonInfo(markConditionDB.CreatedByRef);
      LastChangedBy = new PersonInfo(markConditionDB.LastChangedByRef);

      CreatedOn = markConditionDB.CreatedOn;
      LastChanged = markConditionDB.LastChanged;

      IsDeletedField = markConditionDB.IsDeleted;
      TimeStamp = markConditionDB.TimeStamp.ToArray();
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new ActivityAndMarkNull { PrimaryProperty = RequiredMarkProperty, AffectedProperties = { RequiredActivityProperty } });
      BusinessRules.AddRule(new ActivityAndMarkNull { PrimaryProperty = RequiredActivityProperty, AffectedProperties = { RequiredMarkProperty } });
      BusinessRules.AddRule(new UpdateMarkState { PrimaryProperty = RequiredMarkProperty });
      BusinessRules.AddRule(new UpdateCanCompleteActivity { PrimaryProperty = RequiredMarkProperty });
    }

    private class UpdateCanCompleteActivity : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (MarkConditionBO)context.Target;
        var parent = (target.Parent as MarkConditions);
        if (parent != null)
        {
          var mark = parent.Parent as ActivityMarkBO;
          if (mark != null)
          {
            mark.UpdateActivityCanComplete();
          }
        }
      }
    }

    private class UpdateMarkState : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (MarkConditionBO)context.Target;
        var parent = (target.Parent as MarkConditions);
        if (parent != null)
        {
          var mark = parent.Parent as ActivityMarkBO;
          if (mark != null)
          {
            mark.UpdateState();
            mark.UpdateCanComplete();
          }
        }
      }
    }

    private class ActivityAndMarkNull : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (MarkConditionBO)context.Target;
        var requiredMark = target.ReadProperty(RequiredMarkProperty);
        var requiredActivity = target.ReadProperty(RequiredActivityProperty);
        //if ((requiredMark == null || requiredMark.IsEmpty()) && (requiredActivity == null || requiredActivity.IsEmpty()))
        //  context.AddErrorResult(ValidationErrors.ActivityAndMarkNullError);
        if ((target.Type == ConditionType.MarkRequired) && requiredMark == null)
          context.AddErrorResult(ValidationErrors.RequiredMarkNullError);
        if (target.Type == ConditionType.ActivityRequired && requiredActivity == null)
          context.AddErrorResult(ValidationErrors.RequiredActivityNullError);
        if ((target.Type == ConditionType.SkillRequired) && requiredMark == null)
          context.AddErrorResult(ValidationErrors.RequiredSkillMarkNullError);
      }
    }

    #endregion

    #region  Data Access

    private void Child_Create(MarkInfo mark, ActivityInfo requiredActivity, MarkInfo requiredMark, ConditionType type)
    {
      var currentUser = SecurityChecks.GetCurrentUser();
      using (BypassPropertyChecks)
      {
        MarkConditionId = Guid.NewGuid();
        this.Mark = mark;
        CreatedBy = new PersonInfo(currentUser);
        LastChangedBy = new PersonInfo(currentUser);
        CreatedOn = DateTime.Now;
        RequiredActivity = requiredActivity == null ? null : requiredActivity;
        RequiredMark = requiredMark == null ? null : requiredMark;
        Type = type;
      }
      base.Child_Create();
    }

    private void Child_Fetch(MarkCondition data)
    {
      using (BypassPropertyChecks)
      {
        LoadData(data);
      }
    }

    private void Child_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        Binary lastTimeStamp = null;
        LastChanged = DateTime.Now;
        using (BypassPropertyChecks)
        {
          ctx.DataContext.AddMarkCondition(
            MarkConditionId,
            Mark == null || Mark.IsEmpty() ? (Guid?)null : Mark.Id,
            RequiredMark == null || RequiredMark.IsEmpty() ? (Guid?)null : RequiredMark.Id,
            RequiredActivity == null || RequiredActivity.IsEmpty() ? (Guid?)null : RequiredActivity.Id,
            CreatedBy == null || CreatedBy.IsEmpty() ? (Guid?)null : CreatedBy.Id,
            IsDeletedField,
            CreatedOn,
            LastChanged,
            LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
            ref lastTimeStamp);
          TimeStamp = lastTimeStamp.ToArray();
        }
      }
    }

    private void Child_Update()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        if (IsSelfDirty)
        {
          Binary lastTimeStamp = null;
          LastChangedBy = new PersonInfo(SecurityChecks.GetCurrentUser());
          LastChanged = DateTime.Now;
          using (BypassPropertyChecks)
          {
            ctx.DataContext.UpdateMarkCondition(
            MarkConditionId,
            Mark == null || Mark.IsEmpty() ? (Guid?)null : Mark.Id,
            RequiredMark == null || RequiredMark.IsEmpty() ? (Guid?)null : RequiredMark.Id,
            RequiredActivity == null || RequiredActivity.IsEmpty() ? (Guid?)null : RequiredActivity.Id,
            IsDeletedField,
            LastChanged,
            LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
            TimeStamp,
            ref lastTimeStamp);
            TimeStamp = lastTimeStamp.ToArray();
          }
        }
      }
    }

    private void Child_DeleteSelf()
    {
      using (BypassPropertyChecks)
      {
        IsDeletedField = true;
        Child_Update();
      }
    }

    #endregion

    #region  Factory Methods

    internal static MarkConditionBO NewMarkCondition(MarkInfo mark, ActivityInfo requiredActivity, MarkInfo requiredMark, ConditionType type)
    {
      return Csla.DataPortal.CreateChild<MarkConditionBO>(mark, requiredActivity, requiredMark, type);
    }

    internal static MarkConditionBO NewMarkCondition(MarkInfo mark, ConditionType type)
    {
      return Csla.DataPortal.CreateChild<MarkConditionBO>(mark, null, null, type);
    }

    internal static MarkConditionBO GetMarkCondition(MarkCondition data)
    {
      return Csla.DataPortal.FetchChild<MarkConditionBO>(data);
    }

    private MarkConditionBO()
    { /* require use of factory methods */ }

    #endregion
  }
}